#include "vector"

#include "glowerbound.h"
#include "gbatchInformation.h"





/** -----------------------------------------------------------------------------------------
 * checks if the solution satisfies the constraints
 *
 * @return true if the constraints are satisfied
 */
GLowerBound::GLowerBound(GSupplyLinkProblem* pb) {
  problem = pb ;
  np = problem->getNP() ;
  jobEnd = np-1 ;

  // Find lastDueDate :
  lastDueDate = -1 ;
  for (int j=0;j<np;j++)
    if (problem->getJobDueDate(j)>lastDueDate)
      lastDueDate = problem->getJobDueDate(j) ;
}



/** -----------------------------------------------------------------------------------------
 * evaluates the bin packing L1 lowerbound for the jobs 1..jobStart-1
 *
 * @return L1
 */
double GLowerBound::evaluateBinPackingL1(int jobStart) {
  double sum = 0 ;
  for (int i=0;i<jobStart;i++) 
    sum += problem->getJobSize(i) ;
  
  double L1 = sum/problem->getTransporter(0).getCapacity() ;
  return L1 ;
}



/** -----------------------------------------------------------------------------------------
 * checks if the solution satisfies the constraints
 *
 * @return true if the constraints are satisfied
 */
bool GLowerBound::checkConstraints(GSupplyLinkSolution& sol, int jobStart) {
  assert (sol.nbrBatch>0) ;

  vector<BatchInformation> tabBatchInformation(sol.nbrBatch) ;
  for (int b=0;b<sol.nbrBatch;++b) {
    tabBatchInformation[b].completionTime = lastDueDate ;
  }

  for (int i=jobStart;i<=jobEnd;++i) {
    int batchTabIndice = sol.processingSchedule[i].getBatchIndice() - 1 ;

    assert (batchTabIndice>=0 && batchTabIndice<sol.nbrBatch) ;

    // Constraint (3) : Add the size of job i to the batch where job is is assigned
    tabBatchInformation[batchTabIndice].size += problem->getJobSize(i) ;

    // Constraint (2) : Update the completion time of the batch if the due date of the job is less
    if (tabBatchInformation[batchTabIndice].completionTime > problem->getJobDueDate(i)) {
      tabBatchInformation[batchTabIndice].completionTime = problem->getJobDueDate(i) ;
    }
  }

  double lastDeliveryDate = PLUS_INFINI ;
  for (int b=sol.nbrBatch-1;b>=0;--b) {
    // Constraint (3) : capacity constraint check
    //cout << "tabBatchInformation[b].size=" << tabBatchInformation[b].size << endl ;
    assert (tabBatchInformation[b].size<=problem->getTransporter(0).getCapacity()) ;

    // Constraint (5) : the interval between two successive delivery should be greater or equal to the delivery time
    if (tabBatchInformation[b].completionTime > lastDeliveryDate-problem->getTransporter(0).getDeliveryTime()) {
      tabBatchInformation[b].completionTime = lastDeliveryDate-problem->getTransporter(0).getDeliveryTime() ;
    }
    lastDeliveryDate = tabBatchInformation[b].completionTime ;

  }

  // Constraint (4) : check if all th jobs in a batch have the same completion time as the batch
  for (int i=jobStart;i<=jobEnd;++i) {
    int batchnumber = sol.processingSchedule[i].getBatchIndice()-1 ;

    // Constraint (2) : Update the completion time of the batch if the due date of the job is less
    if (tabBatchInformation[batchnumber].completionTime != sol.processingSchedule[i].getDeliveryCompletionTime()) {
      sol.processingSchedule[i].setDeliveryCompletionTime (tabBatchInformation[batchnumber].completionTime) ;
    }
  }

  return true;
}





/** -----------------------------------------------------------------------------------------
 * evaluates the solution
 *
 * @return evaluation
 */
double GLowerBound::evaluatePartial(GSupplyLinkSolution& sol, int jobStart) {
  double partialEvaluation = -1 ;
  
  GSupplyLinkSolution solsave (sol) ;
  
  if (!checkConstraints(solsave,jobStart)) {
    return -1 ;
  }

  sol.evaluation = solsave.evaluation ;
  sol.evaluationCustomer = solsave.evaluationCustomer ;
  sol.evaluationTransporter = solsave.evaluationTransporter ;
  sol.nbrBatch = solsave.nbrBatch ;
  for (int j=0;j<np;j++)
    sol.processingSchedule[j].setDeliveryCompletionTime(solsave.processingSchedule[j].getDeliveryCompletionTime()) ;

  double partialEvaluationTransporter = sol.nbrBatch * problem->getTransporter(0).getDeliveryCost() ;
  double partialEvaluationCustomer = 0 ;
  for (int i=jobStart;i<=jobEnd;++i) {
    double jobcustcost = problem->getJobHoldingCost(i) ;
    double jobduedate = problem->getJobDueDate(i) ;
    double jobcomptime =  sol.processingSchedule[i].getDeliveryCompletionTime() ;
    partialEvaluationCustomer += jobcustcost*(jobduedate-jobcomptime) ;
  }

  partialEvaluation = partialEvaluationTransporter + partialEvaluationCustomer ;

  return partialEvaluation;
}






/** ###################################################################################
 *
 *                              GLOWERBOUND V0
 * 
 *  ###################################################################################
 */


/** -----------------------------------------------------------------------------------------
 * evaluates the solution
 *
 * @return evaluation
 */
double GLowerBoundV0::evaluate(GSupplyLinkSolution& sol, int jobStart) {
  double eval = evaluatePartial(sol, jobStart) ;
  return eval ;
}







/** ###################################################################################
 *
 *                              GLOWERBOUND V1
 * 
 *  sum of :
 *  1. the L1 lowerbound for the bin packing problem
 *  2. the ....

 *  ###################################################################################
 */


/** -----------------------------------------------------------------------------------------
 * evaluates the solution
 *
 * @return evaluation
 */
double GLowerBoundV1::evaluate(GSupplyLinkSolution& sol, int jobStart) {
  double L1 = evaluateBinPackingL1(jobStart) ;
  double eval = evaluatePartial(sol, jobStart) ;
  return eval+L1 ;
}







